package com.derbysoft.nuke.configkeeper.handler;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.derbysoft.nuke.configkeeper.DefaultInjector;
import com.derbysoft.nuke.configkeeper.api.ApiGatewayResponse;
import com.derbysoft.nuke.configkeeper.api.RPCRequest;
import com.derbysoft.nuke.configkeeper.api.ResponseStatus;
import com.derbysoft.nuke.configkeeper.exceptions.InvalidInputException;
import com.derbysoft.nuke.configkeeper.service.ConfigService;
import com.derbysoft.nuke.configkeeper.utils.JSON;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import static com.derbysoft.nuke.configkeeper.api.ResponseStatus.OK;

public class RpcHandler implements RequestHandler<Map<String, Object>, ApiGatewayResponse> {

    private static final Injector INJECTOR = Guice.createInjector(new DefaultInjector());
    private static final Logger LOG = LogManager.getLogger(RpcHandler.class);

    @Override
    public ApiGatewayResponse handleRequest(Map<String, Object> request, Context context) {
        LOG.info("aws request: " + JSON.get().toJSONString(request));
        String httpMethod = (String) request.get("httpMethod");
        if ("GET".equalsIgnoreCase(httpMethod)) {
            return new ApiGatewayResponse(OK.getStatus(), "<html><head><title>Plastosome JSON-RPC Service</title></head><body><h1>Plastosome JSON-RPC Service</h1><p>Powered by Derbysoft</p></body></html>", Collections.singletonMap("Content-Type", "text/html; charset=UTF-8"));
        }

        try {
            String body = (String) request.get("body");
            LOG.info("rpc request: " + body);
            RPCRequest rpcRequest = JSON.get().toJavaObject(body, RPCRequest.class);
            String method = rpcRequest.getMethod();
            List<Object> params = rpcRequest.getParams();
            LOG.info("rpc method: " + JSON.get().toJSONString(method));
            LOG.info("rpc params: " + JSON.get().toJSONString(params));
            Object result = invoke(method, params);
            LOG.info("rpc response: " + JSON.get().toJSONString(result));
            return ApiGatewayResponse.builder()
                    .setObjectBody(Collections.singletonMap("result", result))
                    .build();
        } catch (Exception e) {
            LOG.error("handleRequest error", e);
            return ApiGatewayResponse.builder()
                    .setStatusCode(ResponseStatus.SERVER_ERROR.getStatus())
                    .setObjectBody(e.getMessage())
                    .build();
        }

    }

    private Object invoke(String method, List<Object> params) {
        ConfigService configService = INJECTOR.getInstance(ConfigService.class);
        try {
            if ("getCurrentTimestamp".equals(method)) {
                return configService.getCurrentTimestamp((String) params.get(0));
            } else if ("available".equals(method)) {
                return configService.available((String) params.get(0));
            } else if ("getAll".equals(method)) {
                return configService.getAll((String) params.get(0));
            } else if ("setProperty".equals(method)) {
                configService.setProperty((String) params.get(0), (String) params.get(1), (String) params.get(2));
                return null;
            } else if ("getProperty".equals(method)) {
                return configService.getProperty((String) params.get(0), (String) params.get(1));
            } else if ("getSources".equals(method)) {
                return configService.getSources();
            } else if ("removeProperty".equals(method)) {
                configService.removeProperty((String) params.get(0), (String) params.get(1));
                return null;
            } else if ("findHistory".equals(method)) {
                return configService.findHistory((String) params.get(0), (Long) params.get(1));
            }
        } catch (IndexOutOfBoundsException | ClassCastException e) {
            throw new InvalidInputException("invalid argument", e);
        }
        throw new UnsupportedOperationException("Unsupported rpc invocation of method[" + method + "]");
    }
}
